
#include <BLabel>
#include <BButton>
#include <BMenuBar>
#include <BGroupBox>
#include <BGridLayout>
#include <BOrthoHandler>

#include "MainWindow.h"

MainWindow::MainWindow()
{
	_transparency = 128;

	initScene();

	_viewer = new BViewer();
	{
		const BSpace& space = _scene->space();
		_camera = new BCamera();
		_camera->setPosition(space.center());
		_camera->setCenter(space.center() + BVector(space.width(), 0, 0));
		_camera->setFovy(36);
		_camera->setNear(1);
		_camera->setFar(space.width() / 5);

		_viewer->setCamera(_camera);
		_viewer->setScene(_scene);
	}

	_mainViewer = new BViewer();
	{
		const BSpace& space = _mainScene->space();

		BCamera* mainCamera = new BCamera();
		mainCamera->setColor(33, 33, 100);
		mainCamera->setPosition(space.max() + space.max() - space.min());
		mainCamera->setCenter(space.center());
		mainCamera->setFovy(36);
		mainCamera->setNear(1);
		mainCamera->setFar(space.radius() * 10);

		_mainHandler = new MainHandler();
		_mainHandler->setTargetCamera(_viewer->camera());
		_mainHandler->setTargetScene(_viewer->scene());

		_mainViewer->setCamera(mainCamera);
		_mainViewer->setScene(_mainScene);
		_mainViewer->setHandler(_mainHandler);
	}

	BMenuBar* menuBar = new BMenuBar();
	{
		BMenu* menu = new BMenu("setting");
		menuBar->addMenu(menu);

		BGroupBox* sceneGroupBox = new BGroupBox("Scene");
		{
			BCheckBox* optimizedCheckBox = new BCheckBox("optimized");
			BVBoxLayout* vlayout = new BVBoxLayout(sceneGroupBox);
			vlayout->addWidget(optimizedCheckBox);
			connect(optimizedCheckBox, Signal_Checked, &MainWindow::slotOptimizedChecked);
		}

		BGroupBox* handlerGroupBox = new BGroupBox("Handler");
		{
			BCheckBox* orthoHandlerCheckBox = new BCheckBox("ortho");
			orthoHandlerCheckBox->setAsset(new BOrthoHandler());

			BRadioBox* sphereHandlerRadioBox = new BRadioBox("sphere");
			sphereHandlerRadioBox->setAsset(new BSphereHandler());

			BRadioBox* strollHandlerRadioBox = new BRadioBox("stroll");
			strollHandlerRadioBox->setAsset(new BStrollHandler());

			BRadioBox* targetHandlerRadioBox = new BRadioBox("target");
			targetHandlerRadioBox->setAsset(new TargetHandler());

			_handlerGroup = new BButtonGroup();
			_handlerGroup->addButton(orthoHandlerCheckBox);
			_handlerGroup->addButton(sphereHandlerRadioBox);
			_handlerGroup->addButton(strollHandlerRadioBox);
			_handlerGroup->addButton(targetHandlerRadioBox);

			BVBoxLayout* vlayout = new BVBoxLayout(handlerGroupBox);
			vlayout->addWidget(orthoHandlerCheckBox);
			vlayout->addWidget(sphereHandlerRadioBox);
			vlayout->addWidget(strollHandlerRadioBox);
			vlayout->addWidget(targetHandlerRadioBox);
		}

		BGroupBox* cameraGroupBox = new BGroupBox("Camera");
		{
			BLabel* orthoLabel = new BLabel("ortho");
			_orthoCheckBox = new BCheckBox();

			BLabel* widthLabel = new BLabel("width");
			_lensWidthSpinBox = new BRealSpinBox();

			BLabel* heightLabel = new BLabel("height");
			_lensHeightSpinBox = new BRealSpinBox();

			BLabel* nearLabel = new BLabel("near");
			_nearSpinBox = new BRealSpinBox();

			BLabel* farLabel = new BLabel("far");
			_farSpinBox = new BRealSpinBox();

			BLabel* fovyLabel = new BLabel("fovy");
			_fovySpinBox = new BRealSpinBox(0.0, 160);

			BLabel* aspectLabel = new BLabel("aspect");
			_aspectSpinBox = new BRealSpinBox(0.0, 10.0);

			BGridLayout* gridLayout = new BGridLayout(cameraGroupBox);
			gridLayout->setWidget(0, 0, orthoLabel);
			gridLayout->setWidget(0, 1, _orthoCheckBox);
			gridLayout->setWidget(1, 0, widthLabel);
			gridLayout->setWidget(1, 1, _lensWidthSpinBox);
			gridLayout->setWidget(2, 0, heightLabel);
			gridLayout->setWidget(2, 1, _lensHeightSpinBox);
			gridLayout->setWidget(3, 0, nearLabel);
			gridLayout->setWidget(3, 1, _nearSpinBox);
			gridLayout->setWidget(4, 0, farLabel);
			gridLayout->setWidget(4, 1, _farSpinBox);
			gridLayout->setWidget(5, 0, fovyLabel);
			gridLayout->setWidget(5, 1, _fovySpinBox);
			gridLayout->setWidget(6, 0, aspectLabel);
			gridLayout->setWidget(6, 1, _aspectSpinBox);
		}

		BVBoxLayout* vlayout = new BVBoxLayout(menu);
		vlayout->addWidget(sceneGroupBox);
		vlayout->addWidget(handlerGroupBox);
		vlayout->addWidget(cameraGroupBox);
	}

	BVBoxLayout* viewerLayout = new BVBoxLayout(_viewer);
	viewerLayout->addSpring();
	viewerLayout->addWidget(menuBar);

	_optionsWidget = new BWidget();
	_optionsWidget->setLayered(true);
	_optionsWidget->setTransparency(_transparency);
	{
		BSpinBox* pointSizeSpinBox = new BSpinBox(1, 8);
		pointSizeSpinBox->setPrefix("point-size : ");
		pointSizeSpinBox->setSuffix("px");
		pointSizeSpinBox->setAlign(Align_Center);

		BCheckBox* showShapeBoxsCheckBox = new BCheckBox("show-shape-boxs");
		showShapeBoxsCheckBox->setChecked(true);

		BCheckBox* showFrustumCheckBox = new BCheckBox("show-frustum");
		showFrustumCheckBox->setChecked(true);

		BCheckBox* showFrustumSpaceCheckBox = new BCheckBox("show-frustum-space");
		showFrustumSpaceCheckBox->setChecked(true);

		BVBoxLayout* vlayout = new BVBoxLayout(_optionsWidget);
		vlayout->addWidget(pointSizeSpinBox);
		vlayout->addWidget(showShapeBoxsCheckBox);
		vlayout->addWidget(showFrustumCheckBox);
		vlayout->addWidget(showFrustumSpaceCheckBox);
		
		connect(pointSizeSpinBox, Signal_ValueEdited, &MainWindow::slotPointSizeEdited);
		connect(showShapeBoxsCheckBox, Signal_Checked, &MainWindow::slotShowShapesChecked);
		connect(showFrustumCheckBox, Signal_Checked, &MainWindow::slotShowFrustumChecked);
		connect(showFrustumSpaceCheckBox, Signal_Checked, &MainWindow::slotShowFrustumSpaceChecked);
	}
	BGridLayout* gridLayout = new BGridLayout(_mainViewer);
	gridLayout->setSpring(0, 0);
	gridLayout->setWidget(1, 1, _optionsWidget);

	BHBoxLayout* splitLayout = new BHBoxLayout(this);
	splitLayout->setSplitable(true);
	splitLayout->setSpacing(6);
	splitLayout->addWidget(_viewer);
	splitLayout->addWidget(_mainViewer);

	freshParams();

	connect(_orthoCheckBox, Signal_Checked, &MainWindow::slotOrthoChecked);
	connect(_handlerGroup, Signal_Checked, &MainWindow::slotHandlerGroupChecked);
	connect(_lensWidthSpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_lensHeightSpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_nearSpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_farSpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_fovySpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_aspectSpinBox, Signal_ValueEdited, &MainWindow::slotParamsChanged);
	connect(_optionsWidget, Signal_Hovered, &MainWindow::slotOptionsHovered);

	_handlerGroup->buttonOfText("target")->setChecked(true);
}
MainWindow::~MainWindow()
{

}

void MainWindow::initScene()
{
	_mainScene = new BScene;
	BGeometry* pointsGeometry = new BGeometry("points");
	BVectorArray* vertices = new BVectorArray(20000);
	BColorArray* colors = new BColorArray(20000);
	pointsGeometry->setVertices(vertices);
	pointsGeometry->setColors(colors);
	pointsGeometry->addElement(Primitive_Points, 0, 20000);
	pointsGeometry->setLighting(false);
	_mainScene->insert(pointsGeometry);

	BReal length = 20000;
	BReal width = 20000;
	_scene = new BScene;
	for (int i = 0; i < 10000; i++)
	{
		BColor color(rand() % 255, rand() % 255, rand() % 255);
		BVector position(float(rand() % 20000), float(rand() % 20000), float(rand() % 20000));

		vertices->set(i, position);
		colors->set(i, color);

		BCylinder* cylinder = new BCylinder(100, 200, 12);
		cylinder->setPosition(position);
		cylinder->setColor(color);
		_scene->insert(cylinder);
	}

	for (int i = 10000; i < 20000; i++)
	{
		BColor color(rand() % 255, rand() % 255, rand() % 255);
		BVector position(float(rand() % 20000), float(rand() % 20000), float(rand() % 20000));

		vertices->set(i, position);
		colors->set(i, color);

		BSphere* sphere = new BSphere(100, 24, 12);
		sphere->setPosition(position);
		sphere->setColor(color);
		_scene->insert(sphere);
	}

	BLight* light = new BLight();
	{
		light->setPosition(100, 100, 100);

		BSphere* sphere = new BSphere(1, 24, 12);
		sphere->setLighting(false);
		sphere->setColor(light->diffuse());
		light->addShape(sphere);
	}
	_scene->insert(light);

	BCube* cube = new BCube(length, width, width * 0.01, 30, 30);
	BColor color(rand() % 255, rand() % 255, rand() % 255);
	cube->setColor(color);
	cube->setLighting(true);
	_scene->insert(cube);
}
void MainWindow::freshParams()
{
	_lensWidthSpinBox->setValue(_camera->lensWidth());
	_lensHeightSpinBox->setValue(_camera->lensHeight());
	_nearSpinBox->setValue(_camera->near());
	_farSpinBox->setValue(_camera->far());
	_fovySpinBox->setValue(_camera->fovy());
	_aspectSpinBox->setValue(_camera->aspect());
}

void MainWindow::slotOptimizedChecked(BObject* object, const BValue& value)
{
	bool optimized = value;
	_scene->setOptimized(optimized);
}

void MainWindow::slotOrthoChecked(BObject* object, const BValue& value)
{
	bool checked = value;
	if (checked)
	{
		_camera->setOrtho(true);
		_camera->setLensWidth(1600);
		_camera->setLensHeight(1000);
		_camera->setNear(1);
		_camera->setFar(5000);
		_lensWidthSpinBox->setEnabled(true);
		_lensHeightSpinBox->setEnabled(true);
	}
	else
	{
		_camera->setOrtho(false);
		_camera->setFovy(36);
		_camera->setNear(1);
		_camera->setFar(5000);
		_lensWidthSpinBox->setEnabled(false);
		_lensHeightSpinBox->setEnabled(false);
	}
	freshParams();
}
void MainWindow::slotHandlerGroupChecked(BObject* object, const BValue& value)
{
	BButton* button = value;
	if (!button)
		return;
	BHandler* handler = button->asset();
	_viewer->setHandler(handler);
}
void MainWindow::slotParamsChanged()
{
	BReal width = _lensWidthSpinBox->value();
	BReal height = _lensHeightSpinBox->value();
	BReal near = _nearSpinBox->value();
	BReal far = _farSpinBox->value();
	BReal fovy = _fovySpinBox->value();
	BReal aspect = _aspectSpinBox->value();

	_camera->setLensWidth(width);
	_camera->setLensHeight(height);
	_camera->setNear(near);
	_camera->setFar(far);
	_camera->setFovy(fovy);
	_camera->setAspect(aspect);

	freshParams();
}

void MainWindow::slotPointSizeEdited(BObject* object, const BValue& value)
{
	int pointSize = value;
	BShape* shape = _mainScene->shape("points");
	if (BGeometry* geometry = dynamic_cast<BGeometry*>(shape))
	{
		geometry->setPointSize(pointSize);
	}
}
void MainWindow::slotShowShapesChecked(BObject* object, const BValue& value)
{
	bool visible = value;
	_mainHandler->setShapeBoxVisible(visible);
}
void MainWindow::slotShowFrustumChecked(BObject* object, const BValue& value)
{
	bool visible = value;
	_mainHandler->setFrustumVisible(visible);
}
void MainWindow::slotShowFrustumSpaceChecked(BObject* object, const BValue& value)
{
	bool visible = value;
	_mainHandler->setFrustumSpaceVisible(visible);
}

void MainWindow::slotOptionsHovered(BObject* object, const BValue& value)
{
	bool hovered = value;
	if(hovered)
		_transparency = 255;
	else
		_transparency = 128;
}

void MainWindow::updateEvent(const BEvent& event)
{
	BByte transparency = _optionsWidget->transparency();
	if (_optionsWidget->transparency() != _transparency)
	{
		int diff = _transparency - transparency;
		BByte change = abs(diff) < 4 ? diff : diff / 4;
		_optionsWidget->setTransparency(transparency + change);
	}
}
